﻿Option Explicit On
Option Strict On

Imports System.Security
Imports System.ServiceModel

Imports Microsoft.VisualBasic.CompilerServices

Imports ACOMDirector
Imports ACOMDirector.Framework.Hardware
Imports ACOMDirector.Framework.Hardware.Remoting
Imports ACOMDirector.Configuration

''' <summary>
''' Application Session
''' </summary>
''' <remarks></remarks>
Friend Class Session
    Implements IDisposable

#Region " Shared Members "

    Private Shared m_Singleton As Session

#End Region

#Region " Private Members "

    Private WithEvents m_StateManager As StateManagerRemotableWrapper
    Private WithEvents m_TrayIcon As NotifyIcon
    Private m_AppContext As ApplicationContext
    Private m_ContextMenu As ContextMenu
    Private WithEvents m_ControlPanel As Form
    Private m_CurrentControlPanelType As String

    'Menu Items
    Private WithEvents m_RetryConnection As MenuItem
    Private WithEvents m_AboutMenuItem As MenuItem
    Private WithEvents m_ConfigurationMenuItem As MenuItem
    Private WithEvents m_ExitMenuItem As MenuItem
    Private WithEvents m_ConnectMenuItem As MenuItem
    Private WithEvents m_DisconnectMenuItem As MenuItem
    Private WithEvents m_KeepOnTopMenuItem As MenuItem
    Private WithEvents m_ControlPanelTypeMenuItem As MenuItem
    Private WithEvents m_ControlPanelTypeLargeMenuItem As MenuItem
    Private WithEvents m_ControlPanelTypeMiniMenuItem As MenuItem

    'Single Instance Stuff
    Private m_MemoryMappedID As String
    Private Const MAX_SESSION_INSTANCES As Integer = 1
    Private SingleInstanceSemaphore As Threading.Semaphore = Nothing ' This needs to live for the life of the application

    Private WithEvents m_WCFHost As ServiceHost 'The WCF Host
    Private WithEvents m_Logger32Interface As Logger32Interface

#End Region

#Region " Private Constructor "

    ''' <summary>
    ''' Private Constructor 
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub New()
    End Sub

#End Region

#Region " Shared Methods "

    ''' <summary>
    ''' Returns the singleton session object
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function GetSession() As Session
        If m_Singleton Is Nothing Then
            m_Singleton = New Session
        End If
        Return m_Singleton
    End Function

#End Region

#Region " Public Properties "

    ''' <summary>
    ''' The ACOM2000A State Manager
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property StateManager As StateManagerRemotableWrapper
        Get
            Return m_StateManager
        End Get
    End Property

#End Region

#Region " Public Methods "

    ''' <summary>
    ''' 
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub Run()

        'Attach Error Handler
        ACOMDirector.Framework.ErrorManagement.GlobalHandlers.HookErrorHandling("James@m0yom.co.uk", My.Application.Info.Version.ToString, "ACOM Director")

        'Single Instance
        Dim applicationInstanceID As String = GetApplicationInstanceID(Reflection.Assembly.GetCallingAssembly())
        m_MemoryMappedID = applicationInstanceID & "Map"

        Dim semaphorePrefix As String = "Local"
        Dim semaphoreName As String = String.Format("{0}\{1}{2}", semaphorePrefix, m_MemoryMappedID, Environment.UserName)
        Dim anotherInstanceRunningForUser As Boolean = True

        Try
            SingleInstanceSemaphore = Threading.Semaphore.OpenExisting(semaphoreName)
        Catch ex As System.Threading.WaitHandleCannotBeOpenedException
            anotherInstanceRunningForUser = False
        End Try

        If (anotherInstanceRunningForUser = False) Then
            SingleInstanceSemaphore = New Threading.Semaphore(0, MAX_SESSION_INSTANCES, semaphoreName)
        End If

        If (anotherInstanceRunningForUser) Then
            MsgBox("Another instance of ACOM Director is already running", MsgBoxStyle.OkOnly Or MsgBoxStyle.Information, "Already Running...")
        End If

        ' If this is not the first instance of the application, then exit
        If anotherInstanceRunningForUser Then
            ACOMDirector.Framework.ErrorManagement.GlobalHandlers.UnhookErrorHandling()
            Exit Sub
        End If

        'Show the splash screen
        Dim SplashScreenTimer As Date = Now
        Dim SplashScreenForm As SplashScreen = Nothing
        Try
            SplashScreenForm = New SplashScreen

            SplashScreenForm.Show()
            Application.DoEvents()

            'Setup the context menu
            m_ContextMenu = New ContextMenu()

            m_AboutMenuItem = New MenuItem("&About...")
            m_ConfigurationMenuItem = New MenuItem("&Configuration...")
            m_ExitMenuItem = New MenuItem("E&xit")
            m_ConnectMenuItem = New MenuItem("Connect")
            m_DisconnectMenuItem = New MenuItem("Disconnect")
            m_KeepOnTopMenuItem = New MenuItem("Keep on top")
            m_ControlPanelTypeMenuItem = New MenuItem("Control Panel Type")
            m_RetryConnection = New MenuItem("&Retry Connection")

            m_ControlPanelTypeLargeMenuItem = New MenuItem("Large")
            m_ControlPanelTypeMiniMenuItem = New MenuItem("Mini")

            m_ControlPanelTypeMenuItem.MenuItems.Add(m_ControlPanelTypeLargeMenuItem)
            m_ControlPanelTypeMenuItem.MenuItems.Add(m_ControlPanelTypeMiniMenuItem)

            SetControlPanelMenuChecked(SystemConfiguration.Instance.ACOMConfiguration.ControlPanelType, True)

            'Setup the Tray Icon
            m_TrayIcon = New NotifyIcon
            m_TrayIcon.Text = "ACOM Director"
            m_TrayIcon.Icon = My.Resources.ACOM_Director
            m_TrayIcon.Visible = True

            'Hide the splash screen, providing it's been displayed for an adequite amount of time
            Do
                If Now.Subtract(SplashScreenTimer).TotalSeconds > 2 Then
                    Exit Do
                End If
                Application.DoEvents()
            Loop
            SplashScreenForm.Close()
        Finally
            If Not SplashScreenForm Is Nothing Then
                If Not SplashScreenForm.IsDisposed Then
                    SplashScreenForm.Dispose()
                End If
            End If
        End Try

        'Create the State Manager
        CreateStateManager()

        'Check if the COM Port is configured properly or not (only if we are running in local mode)
        If Not SystemConfiguration.Instance.ACOMConfiguration.UseRemoteConnection Then
            If SystemConfiguration.Instance.ACOMConfiguration.ACOM2000COMPort = "" Then
                If MsgBox("You have not yet selected the COM port to which the ACOM2000A is connected, would you like to do this now?", MsgBoxStyle.YesNo Or MsgBoxStyle.Information, "Warning") = MsgBoxResult.Yes Then
                    ShowConfiguration()
                End If

                If SystemConfiguration.Instance.ACOMConfiguration.ACOM2000COMPort = "" Then
                    MsgBox("You can select the COM port later by choosing 'Configuration...' from the menu when right clicking on the ACOM Director icon in the system tray", MsgBoxStyle.OkOnly Or MsgBoxStyle.Information, "ACOM Director")
                End If
            End If
        End If

        'Should we show the control panel
        If SystemConfiguration.Instance.ACOMConfiguration.ShowControlPanelOnStartup Then
            ShowControlPanel()
        End If

        'Connect to Logger 32?
        If SystemConfiguration.Instance.ACOMConfiguration.Logger32InterfaceEnabled Then
            StartLogger32Connection()
        End If

        'Start the main message loop
        m_AppContext = New ApplicationContext
        Application.Run(m_AppContext)

        'Kill the Logger32 Interface
        StopLogger32Connection()

        'Stop the WCF Server if we are running one
        StopWCFServer()

        'Dispose of any objects
        m_TrayIcon.Dispose()
        m_TrayIcon = Nothing
        m_AppContext.Dispose()
        m_AppContext = Nothing

        'Remove Error Handler
        ACOMDirector.Framework.ErrorManagement.GlobalHandlers.UnhookErrorHandling()

    End Sub


    ''' <summary>
    ''' Destroys the State Manager object
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub DestroyStateManager()
        StopWCFServer()

        If m_ControlPanel IsNot Nothing AndAlso Not (SystemConfiguration.Instance.ACOMConfiguration.ControlPanelType = m_CurrentControlPanelType) Then
            m_ControlPanel.Close()
            m_ControlPanel.Dispose()
            m_ControlPanel = Nothing
        End If

        If m_StateManager IsNot Nothing Then
            m_StateManager.CloseRemoteConnection()
            m_StateManager = Nothing
        End If

        SetNoStateManagerContextMenu()
    End Sub

    ''' <summary>
    ''' Creates an Instance of the StateManager
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub CreateStateManager()
        If SystemConfiguration.Instance.ACOMConfiguration.UseRemoteConnection Then

            m_TrayIcon.ShowBalloonTip(2000, "Connecting...", "Attempting to connect to remote instance", ToolTipIcon.Info)

            'Build the connection address
            Dim EndpointAddress As String
            Select Case SystemConfiguration.Instance.ACOMConfiguration.RemoteConnectionMethod
                Case "TCP"
                    EndpointAddress = "net.tcp://"
                Case "HTTP"
                    EndpointAddress = "http://"
                Case Else 'Assume TCP
                    EndpointAddress = "net.tcp://"
            End Select
            EndpointAddress &= SystemConfiguration.Instance.ACOMConfiguration.RemoteConnectionAddress & ":"
            EndpointAddress &= SystemConfiguration.Instance.ACOMConfiguration.RemoteConnectionPort.ToString
            EndpointAddress &= "/ACOM2000StateManager"

            'Create the chosen Binding object
            Dim Binding As Channels.Binding
            Select Case SystemConfiguration.Instance.ACOMConfiguration.RemoteConnectionMethod
                Case "TCP"
                    Binding = New NetTcpBinding(SecurityMode.None)
                Case "HTTP"
                    Binding = New WSDualHttpBinding '(WSDualHttpSecurityMode.None)
                Case Else 'Assume TCP
                    Binding = New NetTcpBinding(SecurityMode.None)
            End Select

            m_StateManager = StateManagerRemotableWrapper.ConnectStateManagerWrapper(Binding, New EndpointAddress(EndpointAddress))

            If m_StateManager IsNot Nothing Then
                m_TrayIcon.ShowBalloonTip(2000, "Connected", "A connection has been established to the remote instance", ToolTipIcon.Info)
            End If
        Else
            m_StateManager = StateManagerRemotableWrapper.CreateStateManagerWrapper(SystemConfiguration.Instance.ACOMConfiguration.ACOM2000COMPort, SystemConfiguration.Instance.ACOMConfiguration.SupportsRemotePowerOn, SystemConfiguration.Instance.ACOMConfiguration.DetermineOperateModeOnConnection, DirectCast(SystemConfiguration.Instance.ACOMConfiguration.RemotePowerOnMechanism, RemotePowerOnSystem))
        End If

        'Did we manage to create the state manager?
        If m_StateManager Is Nothing Then
            m_TrayIcon.ShowBalloonTip(5000, "Error connecting to ACOM Director remote instance", "A problem occured while connecting to the remote instance of ACOM Director" & vbCrLf & vbCrLf & "Please check that the configuration is correct and retry the connection", ToolTipIcon.Error)

            'Setup the context menu
            SetNoStateManagerContextMenu()
        Else

            'Setup the context menu
            SetNormalContextMenu()

            If m_StateManager IsNot Nothing Then
                If Not m_StateManager.State.CanConnect Then
                    m_ConnectMenuItem.Enabled = False
                ElseIf Not m_StateManager.COMPortOpen Then
                    m_ConnectMenuItem.Enabled = True
                End If
                m_DisconnectMenuItem.Enabled = m_StateManager.UIState.DisconnectEnabled
            End If

            'Should we host a server?
            If Not SystemConfiguration.Instance.ACOMConfiguration.UseRemoteConnection And SystemConfiguration.Instance.ACOMConfiguration.AllowRemoteConnections Then
                StartWCFServer()
            End If
        End If
    End Sub

    Private Sub SetNormalContextMenu()
        m_TrayIcon.ContextMenu = Nothing
        m_ContextMenu.MenuItems.Clear()
        m_ContextMenu.MenuItems.Add(m_ConfigurationMenuItem)
        m_ContextMenu.MenuItems.Add(m_ControlPanelTypeMenuItem)
        m_ContextMenu.MenuItems.Add(m_KeepOnTopMenuItem)
        m_ContextMenu.MenuItems.Add("-")
        m_ContextMenu.MenuItems.Add(m_ConnectMenuItem)
        m_ContextMenu.MenuItems.Add(m_DisconnectMenuItem)
        m_ContextMenu.MenuItems.Add("-")
        m_ContextMenu.MenuItems.Add(m_AboutMenuItem)
        m_ContextMenu.MenuItems.Add("-")
        m_ContextMenu.MenuItems.Add(m_ExitMenuItem)
        m_TrayIcon.ContextMenu = m_ContextMenu
    End Sub

    Private Sub SetNoStateManagerContextMenu()
        m_TrayIcon.ContextMenu = Nothing
        m_ContextMenu.MenuItems.Clear()
        m_ContextMenu.MenuItems.Add(m_ConfigurationMenuItem)
        m_ContextMenu.MenuItems.Add("-")
        m_ContextMenu.MenuItems.Add(m_RetryConnection)
        m_ContextMenu.MenuItems.Add("-")
        m_ContextMenu.MenuItems.Add(m_ExitMenuItem)
        m_TrayIcon.ContextMenu = m_ContextMenu
    End Sub

    ''' <summary>
    ''' Stops the WCF Server
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub StopWCFServer()
        If Not m_WCFHost Is Nothing Then
            m_WCFHost.Close()
            m_WCFHost = Nothing

            m_TrayIcon.ShowBalloonTip(2000, "Remote server stopped", "ACOM Director server is stopped", ToolTipIcon.Info)
        End If
    End Sub

    Private Sub StartLogger32Connection()

        StopLogger32Connection()

        m_Logger32Interface = New Logger32Interface
        m_Logger32Interface.Connect()

    End Sub

    Private Sub StopLogger32Connection()

        If m_Logger32Interface IsNot Nothing Then
            m_Logger32Interface.Disconnect()
            m_Logger32Interface.Dispose()
            m_Logger32Interface = Nothing
        End If

    End Sub

    ''' <summary>
    ''' Starts the WCF Server
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub StartWCFServer()
        'Ensure that we don't already have a server running
        StopWCFServer()

        'Build the connection address
        Dim EndpointAddress As String
        Select Case SystemConfiguration.Instance.ACOMConfiguration.RemoteConnectionMethod
            Case "TCP"
                EndpointAddress = "net.tcp://"
            Case "HTTP"
                EndpointAddress = "http://"
            Case Else 'Assume TCP
                EndpointAddress = "net.tcp://"
        End Select
        EndpointAddress &= "localhost" & ":"
        EndpointAddress &= SystemConfiguration.Instance.ACOMConfiguration.RemoteConnectionPort.ToString

        'Create the chosen Binding object
        Dim Binding As Channels.Binding
        Select Case SystemConfiguration.Instance.ACOMConfiguration.RemoteConnectionMethod
            Case "TCP"
                Binding = New NetTcpBinding(SecurityMode.None)
            Case "HTTP"
                Binding = New WSDualHttpBinding(WSDualHttpSecurityMode.None)
            Case Else 'Assume TCP
                Binding = New NetTcpBinding(SecurityMode.None)
        End Select

        'Setup the WCF Server
        m_WCFHost = New ServiceHost(m_StateManager.Proxy, New Uri() {New Uri(EndpointAddress)})
        m_WCFHost.AddServiceEndpoint(GetType(IStateManager), Binding, "ACOM2000StateManager")

        m_WCFHost.Open()

        m_TrayIcon.ShowBalloonTip(2000, "Remote server now running", "ACOM Director server is now running", ToolTipIcon.Info)
    End Sub

    ''' <summary>
    ''' Shows the configuration dialog and handles any changes in the configuration
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub ShowConfiguration()

        Using Dialog As New ConfigurationDialog

            Dim CurrentUseRemoteConnection As Boolean = SystemConfiguration.Instance.ACOMConfiguration.UseRemoteConnection
            Dim CurrentRemotePort As Integer = SystemConfiguration.Instance.ACOMConfiguration.RemoteConnectionPort
            Dim CurrentRemoteAddress As String = SystemConfiguration.Instance.ACOMConfiguration.RemoteConnectionAddress
            Dim CurrentRemoteConnectionMethod As String = SystemConfiguration.Instance.ACOMConfiguration.RemoteConnectionMethod

            If Dialog.ShowDialog() = Windows.Forms.DialogResult.OK Then

                'Has the mode changed?
                If Not CurrentUseRemoteConnection = SystemConfiguration.Instance.ACOMConfiguration.UseRemoteConnection Then
                    'Are we now using a remote connection?
                    If SystemConfiguration.Instance.ACOMConfiguration.UseRemoteConnection Then

                        'Destroy the current state manager
                        DestroyStateManager()

                        'Remove the context menu while we are trying to connect
                        m_TrayIcon.ContextMenu = Nothing

                        'Try to connect to the new state manager
                        CreateStateManager()

                    Else
                        'We were using a remote connection, so stop it and create a local connection
                        'Destroy the current state manager
                        DestroyStateManager()

                        'Create the new local state manager
                        CreateStateManager()

                    End If
                Else
                    If SystemConfiguration.Instance.ACOMConfiguration.UseRemoteConnection Then
                        If Not CurrentRemotePort = SystemConfiguration.Instance.ACOMConfiguration.RemoteConnectionPort Or _
                            Not CurrentRemoteAddress = SystemConfiguration.Instance.ACOMConfiguration.RemoteConnectionAddress Or _
                            Not CurrentRemoteConnectionMethod = SystemConfiguration.Instance.ACOMConfiguration.RemoteConnectionMethod Then

                            'Destroy the current state manager
                            DestroyStateManager()

                            'Remove the context menu while we are trying to connect
                            m_TrayIcon.ContextMenu = Nothing

                            'Try to connect to the new state manager
                            CreateStateManager()
                        End If
                    Else
                        Dim StartServer As Boolean = False
                        If SystemConfiguration.Instance.ACOMConfiguration.AllowRemoteConnections Then
                            StartServer = True
                        End If

                        If Not CurrentRemoteConnectionMethod = SystemConfiguration.Instance.ACOMConfiguration.RemoteConnectionMethod Or _
                            Not CurrentRemotePort = SystemConfiguration.Instance.ACOMConfiguration.RemoteConnectionPort Then
                            StartServer = True
                        End If

                        If StartServer Then
                            StartWCFServer()
                        End If
                    End If

                End If

                If Not SystemConfiguration.Instance.ACOMConfiguration.UseRemoteConnection Then
                    m_StateManager.COMPort = SystemConfiguration.Instance.ACOMConfiguration.ACOM2000COMPort
                End If

                If SystemConfiguration.Instance.ACOMConfiguration.Logger32InterfaceEnabled Then
                    StartLogger32Connection()
                Else
                    StopLogger32Connection()
                End If

            End If
        End Using

    End Sub

#End Region

#Region " Single Instance Methods "

    <System.Security.SecurityCritical()> _
    Private Shared Function GetApplicationInstanceID(Entry As System.Reflection.Assembly) As String

        Dim SecuritySet As PermissionSet = New PermissionSet(Permissions.PermissionState.None)
        SecuritySet.AddPermission(New Permissions.FileIOPermission(Permissions.PermissionState.Unrestricted))
        SecuritySet.AddPermission(New Permissions.SecurityPermission(Permissions.SecurityPermissionFlag.UnmanagedCode))
        SecuritySet.Assert()

        Dim typeLibGuidForAssembly As Guid = Runtime.InteropServices.Marshal.GetTypeLibGuidForAssembly(Entry)
        Dim strArray() As String = Entry.GetName().Version.ToString().Split(Conversions.ToCharArrayRankOne("."))
        PermissionSet.RevertAssert()

        Return (typeLibGuidForAssembly.ToString() + strArray(0) & "." & strArray(1))
    End Function

#End Region

#Region " Event Handlers "

    Private Sub m_AboutMenuItem_Click(sender As Object, e As System.EventArgs) Handles m_AboutMenuItem.Click
        Using Dialog As New AboutDialog
            Dialog.ShowDialog()
        End Using
    End Sub

    Private Sub m_ConfigurationMenuItem_Click(sender As Object, e As System.EventArgs) Handles m_ConfigurationMenuItem.Click
        ShowConfiguration()
    End Sub

    Private Sub m_ExitMenuItem_Click(sender As Object, e As System.EventArgs) Handles m_ExitMenuItem.Click
        If m_ControlPanel IsNot Nothing Then
            m_ControlPanel.Close()
        End If

        If m_StateManager IsNot Nothing Then
            m_StateManager.Disconnect()
        End If

        m_AppContext.ExitThread()
    End Sub

    Private Sub m_StateManager_ChannelFault(sender As Object, e As System.EventArgs) Handles m_StateManager.ChannelFault
        'When running in remote mode a channel fault occured
        m_TrayIcon.ShowBalloonTip(2000, "Remote Connection Lost", "The connection to the remote instance has been lost", ToolTipIcon.Error)
        DestroyStateManager()
    End Sub

    Private Sub m_StateManager_StateChanged(sender As Object, e As System.EventArgs) Handles m_StateManager.StateChanged
        If Not m_StateManager.State.CanConnect Then
            m_ConnectMenuItem.Enabled = False
        ElseIf Not m_StateManager.COMPortOpen Then
            m_ConnectMenuItem.Enabled = True
        End If

        m_ConnectMenuItem.Enabled = m_StateManager.UIState.ConnectEnabled
        m_DisconnectMenuItem.Enabled = m_StateManager.UIState.DisconnectEnabled
        Application.DoEvents()
    End Sub

    Private Sub m_TrayIcon_MouseDoubleClick(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles m_TrayIcon.MouseDoubleClick

        If m_StateManager Is Nothing Then
            If MsgBox("ACOM Director is not currently connected to the remote instance, would you like to connect now?", MsgBoxStyle.Question Or MsgBoxStyle.YesNo, "Connect?") = MsgBoxResult.Yes Then
                CreateStateManager()
            End If
        End If

        ShowControlPanel()
    End Sub


    Private Sub m_KeepOnTopMenuItem_Click(sender As Object, e As System.EventArgs) Handles m_KeepOnTopMenuItem.Click
        If SystemConfiguration.Instance.ACOMConfiguration.KeepWindowOnTop Then
            SystemConfiguration.Instance.ACOMConfiguration.KeepWindowOnTop = False
            SystemConfiguration.Instance.ACOMConfiguration.Save()
            m_KeepOnTopMenuItem.Checked = False
        Else
            SystemConfiguration.Instance.ACOMConfiguration.KeepWindowOnTop = True
            SystemConfiguration.Instance.ACOMConfiguration.Save()
            m_KeepOnTopMenuItem.Checked = True
        End If

        If m_ControlPanel IsNot Nothing Then
            m_ControlPanel.TopMost = SystemConfiguration.Instance.ACOMConfiguration.KeepWindowOnTop
        End If
    End Sub

    Private Sub m_ConnectMenuItem_Click(sender As Object, e As System.EventArgs) Handles m_ConnectMenuItem.Click
        m_StateManager.Connect()
    End Sub

    Private Sub m_DisconnectMenuItem_Click(sender As Object, e As System.EventArgs) Handles m_DisconnectMenuItem.Click
        m_StateManager.Disconnect()
    End Sub

    Private Sub m_WCFHost_Faulted(sender As Object, e As System.EventArgs) Handles m_WCFHost.Faulted
        'Restart the WCF Server
        StartWCFServer()
    End Sub

    Private Sub m_RetryConnection_Click(sender As Object, e As System.EventArgs) Handles m_RetryConnection.Click
        CreateStateManager()
    End Sub

#End Region

#Region " Control Panel Management "

    Private Sub m_ControlPanelTypeLargeMenuItem_Click(sender As Object, e As System.EventArgs) Handles m_ControlPanelTypeLargeMenuItem.Click
        ChangeControlPanelType("Large")
    End Sub

    Private Sub m_ControlPanelTypeMiniMenuItem_Click(sender As Object, e As System.EventArgs) Handles m_ControlPanelTypeMiniMenuItem.Click
        ChangeControlPanelType("Mini")
    End Sub

    ''' <summary>
    ''' Ensures the control panel is disposed of when it's closed
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub m_ControlPanel_FormClosed(sender As Object, e As System.Windows.Forms.FormClosedEventArgs) Handles m_ControlPanel.FormClosed
        If m_ControlPanel IsNot Nothing Then
            m_ControlPanel.Dispose()
            m_ControlPanel = Nothing
        End If
    End Sub

    ''' <summary>
    ''' Checks if a rectangle is visible on any of the screens
    ''' </summary>
    ''' <param name="rect"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Shared Function IsVisibleOnAnyScreen(rect As Rectangle) As Boolean
        For Each item As Screen In Screen.AllScreens
            If item.WorkingArea.IntersectsWith(rect) Then
                Return True
            End If
        Next

        Return False
    End Function

    ''' <summary>
    ''' Changes the control panel type
    ''' </summary>
    ''' <param name="NewType"></param>
    ''' <remarks></remarks>
    Private Sub ChangeControlPanelType(NewType As String)

        Dim WasVisible As Boolean = False
        If m_ControlPanel IsNot Nothing Then
            WasVisible = m_ControlPanel.Visible
            m_ControlPanel.Close()
        End If

        SetControlPanelMenuChecked(SystemConfiguration.Instance.ACOMConfiguration.ControlPanelType, False)

        SystemConfiguration.Instance.ACOMConfiguration.ControlPanelType = NewType
        SystemConfiguration.Instance.ACOMConfiguration.Save()

        SetControlPanelMenuChecked(SystemConfiguration.Instance.ACOMConfiguration.ControlPanelType, True)

        If WasVisible Then
            ShowControlPanel()
        End If

    End Sub

    ''' <summary>
    ''' Sets the Control Panel Type menu items checked state
    ''' </summary>
    ''' <param name="ControlPanelType"></param>
    ''' <param name="State"></param>
    ''' <remarks></remarks>
    Private Sub SetControlPanelMenuChecked(ByVal ControlPanelType As String, State As Boolean)

        Select Case ControlPanelType
            Case "Large"
                m_ControlPanelTypeLargeMenuItem.Checked = State
            Case "Mini"
                m_ControlPanelTypeMiniMenuItem.Checked = State
        End Select

    End Sub

    ''' <summary>
    ''' Shows the control panel
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub ShowControlPanel()
        'Don't show control panel if we don't have a state manager
        If m_StateManager Is Nothing Then
            Return
        End If

        If m_ControlPanel IsNot Nothing AndAlso Not (SystemConfiguration.Instance.ACOMConfiguration.ControlPanelType = m_CurrentControlPanelType) Then
            m_ControlPanel.Close()
        End If

        If m_ControlPanel Is Nothing Then
            Dim StartPositionX As Integer = -1
            Dim StartPositionY As Integer = -1

            Select Case SystemConfiguration.Instance.ACOMConfiguration.ControlPanelType
                Case "Large"
                    StartPositionX = SystemConfiguration.Instance.ACOMConfiguration.LargeControlPanelLocationX
                    StartPositionY = SystemConfiguration.Instance.ACOMConfiguration.LargeControlPanelLocationY

                    m_ControlPanel = New ControlPanelLarge
                Case "Mini"
                    StartPositionX = SystemConfiguration.Instance.ACOMConfiguration.MiniControlPanelLocationX
                    StartPositionY = SystemConfiguration.Instance.ACOMConfiguration.MiniControlPanelLocationY

                    m_ControlPanel = New ControlPanelMini
            End Select
            m_CurrentControlPanelType = SystemConfiguration.Instance.ACOMConfiguration.ControlPanelType

            'Set the Start Location of the control panel
            If Not (StartPositionX = -1 And StartPositionY = -1) AndAlso IsVisibleOnAnyScreen(New Rectangle(New Point(StartPositionX, StartPositionY), m_ControlPanel.Size)) Then
                'We have a stored position that is valid for the current resolution and number of screens
                m_ControlPanel.StartPosition = FormStartPosition.Manual
                m_ControlPanel.Location = New Drawing.Point(StartPositionX, StartPositionY)
            Else
                'We either don't have a stored position or the stored position isn't valid for the current resolution and number of screens
                m_ControlPanel.StartPosition = FormStartPosition.WindowsDefaultLocation
            End If

            m_ControlPanel.TopMost = SystemConfiguration.Instance.ACOMConfiguration.KeepWindowOnTop
            m_ControlPanel.Show()
            m_ControlPanel.BringToFront()
        Else
            m_ControlPanel.Show()
            m_ControlPanel.BringToFront()
        End If

    End Sub

#End Region

#Region "IDisposable Support"

    Private disposedValue As Boolean ' To detect redundant calls

    ' IDisposable
    Protected Overridable Sub Dispose(disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then

                If m_RetryConnection IsNot Nothing Then m_RetryConnection.Dispose()
                If m_AboutMenuItem IsNot Nothing Then m_AboutMenuItem.Dispose()
                If m_ConfigurationMenuItem IsNot Nothing Then m_ConfigurationMenuItem.Dispose()
                If m_ExitMenuItem IsNot Nothing Then m_ExitMenuItem.Dispose()
                If m_ConnectMenuItem IsNot Nothing Then m_ConnectMenuItem.Dispose()
                If m_DisconnectMenuItem IsNot Nothing Then m_DisconnectMenuItem.Dispose()
                If m_KeepOnTopMenuItem IsNot Nothing Then m_KeepOnTopMenuItem.Dispose()
                If m_ControlPanelTypeMenuItem IsNot Nothing Then m_ControlPanelTypeMenuItem.Dispose()
                If m_ControlPanelTypeLargeMenuItem IsNot Nothing Then m_ControlPanelTypeLargeMenuItem.Dispose()
                If m_ControlPanelTypeMiniMenuItem IsNot Nothing Then m_ControlPanelTypeMiniMenuItem.Dispose()

                If m_ControlPanel IsNot Nothing Then m_ControlPanel.Dispose()
                If m_ContextMenu IsNot Nothing Then m_ContextMenu.Dispose()
                If m_TrayIcon IsNot Nothing Then m_TrayIcon.Dispose()

                If m_Logger32Interface IsNot Nothing Then m_Logger32Interface.Dispose()

                If SingleInstanceSemaphore IsNot Nothing Then SingleInstanceSemaphore.Close()
                If m_AppContext IsNot Nothing Then m_AppContext.Dispose()

            End If

        End If
        Me.disposedValue = True
    End Sub

    ' This code added by Visual Basic to correctly implement the disposable pattern.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

#End Region

    Private Sub m_Logger32Interface_Logger32FrequencyChanged(sender As Object, e As Logger32FrequencyChangedEventArgs) Handles m_Logger32Interface.Logger32FrequencyChanged
        'Make sure the notification is coming from the right radio number
        If e.RadioNumber = SystemConfiguration.Instance.ACOMConfiguration.Logger32RadioNumber Then
            m_StateManager.SetFrequencyBand(e.Frequency)
        End If
    End Sub

End Class
